package com.cskaoyan.util.beanutils;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @Author: 冯子靖
 * @Description:   实体类属性值拷贝
 * @Date: 2022/7/16 15:03
 */
public class BeanUtil {
    /**
     * 工具使用说明: 两个实体类中,一般在VO实体类中的属性上添加@FieldAlias别名注解,和与数据库交互的PO属性产生映射关系.
     *              特别注意:需要在实体类中加入getter和setter方法,或者添加@Data注解
     * @param originBean 属性值提供实体类(C)
     * @param targetBean 待copy实体类(V)
     */
    public static void copyBean(Object originBean, Object targetBean) {
        Map<String, Object> originFieldKeyWithValueMap = new HashMap<>(16);
        PropertyDescriptor propertyDescriptor = null;
        //生成源bean的属性及其值的字典
        operateBeanFieldWithValue(originBean,
                propertyDescriptor,
                originFieldKeyWithValueMap,
                originBean.getClass(),
                (bean, descriptor, realFieldName, fieldWithMethodMap) -> {
                    try {
                        //获取当前属性的get方法
                        Method method = descriptor.getReadMethod();
                        //设置值
                        Object value = method.invoke(bean);
                        //将源对象值缓存设置值
                        fieldWithMethodMap.put(realFieldName, value);
                    } catch (IllegalAccessException e) {
                        System.err.println("【源对象】异常:" + realFieldName + "的get方法执行失败！");
                    } catch (InvocationTargetException e) {
                        System.err.println("【源对象】异常:" + realFieldName + "的get方法执行失败！");
                    }
                }
        );
        //设置目标bean的属性值
        operateBeanFieldWithValue(targetBean, propertyDescriptor, originFieldKeyWithValueMap, targetBean.getClass(),
                (bean, descriptor, realFieldName, fieldWithMethodMap) -> {
                    try {
                        //获取当前属性的set方法
                        Method method = descriptor.getWriteMethod();
                        method.invoke(bean, fieldWithMethodMap.get(realFieldName));
                    } catch (IllegalAccessException e) {
                        System.err.println("【目标对象】异常:" + realFieldName + "的set方法执行失败！");
                    } catch (InvocationTargetException e) {
                        System.err.println("【目标对象】异常:" + realFieldName + "的set方法执行失败！");
                    }
                });

    }

    private static void operateBeanFieldWithValue(Object bean,
                                                  PropertyDescriptor descriptor,
                                                  Map<String, Object> originFieldNameWithValueMap,
                                                  Class<?> beanClass,
                                                  CVFunction cvFunction
    ) {
        /**如果不存在超类，那么跳出循环*/
        if (beanClass.getSuperclass() == null) {
            return;
        }
        Field[] fieldList = beanClass.getDeclaredFields();
        for (Field field : fieldList) {
            try {
                /*获取属性上的注解。如果不存在，使用属性名，如果存在使用注解名*/
                FieldAlias fieldAlias = field.getAnnotation(FieldAlias.class);
                String realFieldName = Objects.isNull(fieldAlias) ? field.getName() : fieldAlias.value();
                //初始化
                descriptor = new PropertyDescriptor(field.getName(), beanClass);
                cvFunction.apply(bean, descriptor, realFieldName, originFieldNameWithValueMap);
            } catch (IntrospectionException e) {
                System.err.println("【源对象】异常:" + field.getName() + "不存在对应的get方法，无法参与拷贝！");
            }
        }
        //生成超类 属性-value
        operateBeanFieldWithValue(bean, descriptor, originFieldNameWithValueMap, beanClass.getSuperclass(), cvFunction);
    }
}
